﻿using System;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using System.Collections.Generic;

namespace ShevaEngine.Core.Modules.Materials
{
    /// <summary>
    /// Material profile.
    /// </summary>
    public class GraphicsMaterial
    {
        /// <summary>Effect filename.</summary>
        public string EffectName;
        /// <summary>XNA effect.</summary>
        [ContentSerializerIgnore]
        public Effect Effect;
        /// <summary>Textures state.</summary>
        [ContentSerializerIgnore]
        public Textures DefaultTexturesState;

        /// <summary>Parallax enabled.</summary>
        //public bool ParallaxEnabled;


        /// <summary>Specular power.</summary>
        public Single SpecularPower;

        /// <summary>Lighting enabled.</summary>
        public bool LightingEnabled;
        /// <summary>Cast shadows.</summary>
        public bool CastShadows;
        /// <summary>Receive shadows.</summary>
        public bool ReceiveShadows;

        /// <summary>Blend state type.</summary>
        public BlendStateType BlendStateType;
        /// <summary>Alpha blend function.</summary>
        public BlendFunction AlphaBlendFunction;
        /// <summary>Alpha source blend.</summary>
        public Blend AlphaSourceBlend;
        /// <summary>Alpha destination blend.</summary>
        public Blend AlphaDestinationBlend;

        /// <summary>Default params id.</summary>
        [ContentSerializerIgnore]
        public BlendState BlendState;
        
        /// <summary>Depth stencil state.</summary>
        public DepthStencilStateType DepthStencilStateType;
        /// <summary>Depth buffer enable.</summary>
        public bool DepthBufferEnable;
        /// <summary>Depth buffer function.</summary>
        public CompareFunction DepthBufferFunction;
        /// <summary>Depth buffer write enable.</summary>
        public bool DepthBufferWriteEnable;

        /// <summary>Default params id.</summary>
        [ContentSerializerIgnore]
        public DepthStencilState DepthStencilState;

        /// <summary>Parameters manager.</summary>
        [ContentSerializerIgnore]
        public GraphicsMaterialParametersManager ParametersManager;

        /// <summary>Default params id.</summary>
        [ContentSerializerIgnore]
        private UInt16 DefaultParamsId;
        /// <summary>Default parameters.</summary>
        public GraphicsMaterialParametersList DefaultParameters;        

        /// <summary>
        /// Constructor.
        /// </summary>
        public GraphicsMaterial()
        {
            this.ParametersManager = new GraphicsMaterialParametersManager();
            this.BlendStateType = BlendStateType.Opaque;
            this.DepthStencilStateType = DepthStencilStateType.Default;
        }

        /// <summary>
        /// Initialization method.
        /// </summary>
        internal void Init()
        {
            this.Effect = MaterialManager.Instance.GetEffect(this.EffectName);

            this.DefaultParamsId = this.ParametersManager.GetParametersID(this.DefaultParameters);

            switch (this.BlendStateType)
            {
                case BlendStateType.Additive:
                    this.BlendState = BlendState.Additive;
                    break;
                case BlendStateType.AlphaBlend:
                    this.BlendState = BlendState.AlphaBlend;
                    break;
                case BlendStateType.NonPremultiplied:
                    this.BlendState = BlendState.NonPremultiplied;
                    break;
                case BlendStateType.Opaque:
                    this.BlendState = BlendState.Opaque;
                    break;
                case BlendStateType.UserDefined:
                    this.BlendState = MaterialManager.Instance.GetBlendState(
                        this.AlphaBlendFunction, this.AlphaSourceBlend, this.AlphaDestinationBlend);
                    break;                
            }

            switch (this.DepthStencilStateType)
            {
                case DepthStencilStateType.Default:
                    this.DepthStencilState = DepthStencilState.Default;
                    break;
                case DepthStencilStateType.DepthRead:
                    this.DepthStencilState = DepthStencilState.DepthRead;
                    break;
                case DepthStencilStateType.None:
                    this.DepthStencilState = DepthStencilState.None;
                    break;
                case DepthStencilStateType.UserDefined:
                    this.DepthStencilState = MaterialManager.Instance.GetDepthStencilState(
                        this.DepthBufferEnable, this.DepthBufferFunction, this.DepthBufferWriteEnable);
                    break;                
            }
        }

        /// <summary>
        /// Method gets parameters id for parameters.
        /// </summary>
        /// <param name="parameters"></param>
        /// <returns></returns>
        public UInt16 GetMaterialParametersId(GraphicsMaterialParametersList parameters)
        {
            return this.ParametersManager.GetParametersID(parameters);
        }

        /// <summary>
        /// Method sets material parameters.
        /// </summary>
        /// <param name="id"></param>
        public void SetDefaultGraphicsMaterialParameters()
        {
            this.ParametersManager[this.DefaultParamsId].SetParameters(this);
        }

        /// <summary>
        /// Method sets material parameters.
        /// </summary>
        /// <param name="id"></param>
        public void SetGraphicsMaterialParameters(UInt16 id)
        {
            if (ShevaEngine.Instance.GraphicsDevice.BlendState != this.BlendState)
                ShevaEngine.Instance.GraphicsDevice.BlendState = this.BlendState;

            if (ShevaEngine.Instance.GraphicsDevice.DepthStencilState != this.DepthStencilState)
                ShevaEngine.Instance.GraphicsDevice.DepthStencilState = this.DepthStencilState;

            this.ParametersManager[id].SetParameters(this);
        }
    }

    /// <summary>
    /// Blend state type.
    /// </summary>
    public enum BlendStateType
    {
        Additive,
        AlphaBlend,
        NonPremultiplied,
        Opaque,
        UserDefined,
    }

    /// <summary>
    /// Depth stencil state type.
    /// </summary>
    public enum DepthStencilStateType
    {
        Default,
        DepthRead,
        None,
        UserDefined,
    }
}
